/*********************************************************************************
  *Copyright (c)    2021   xldeng
  *FileName:        log.h
  *Author:          xldeng
  *Version:         1.0
  *Date:            2021/2/24 10:42
  *Description:     日志模块封装
  *Others:
  *Function List:
     1.…………
  *History:
     1.Date:
       Author:
       Modification:
**********************************************************************************/

#ifndef SYLAR_LOG_H
#define SYLAR_LOG_H

#include <string>
#include <cstdint>
#include <memory>
#include <list>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <cstdarg>
#include "singleton.h"
#include "util.h"
#include "thread.h"

// 定义宏，方便运用，只需要传入logger即可，并且支持流输入
#define SYLAR_LOG_LEVEL(logger, level) \
    if(logger->getLevel() <= level)    \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, __FILE__, __LINE__,0 ,sylar::GetThreadId() ,sylar::GetFiberId(),\
        time(0), sylar::Thread::GetName()))).getSS()

#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger,sylar::LogLevel::DEBUG)

#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)

#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)

#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)

#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)
// 定义特定格式的日志宏
#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...) \
    if(logger->getLevel() <= level) \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
                        __FILE__, __LINE__, 0, sylar::GetThreadId(),\
                sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)

#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_INFO(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_WARN(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)

// 获取根日志器宏
#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
// 获取/生成特定名字日志器宏
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)

namespace sylar {
    class Logger;

    /*
     *  日志级别
     */
    class LogLevel {
    public:
        enum Level {
            /// 未知级别
            UNKNOWN = 0,
            /// DEBUG 级别
            DEBUG = 1,
            /// INFO 级别
            INFO = 2,
            /// WARN 级别
            WARN = 3,
            /// ERROR 级别
            ERROR = 4,
            /// FATAL 级别
            FATAL = 5
        };

        static const char *ToString(LogLevel::Level level);

        static LogLevel::Level FromString(const std::string &str);
    };

    /*
     * 日志事件
     */
    class LogEvent {
    public:
        using ptr = std::shared_ptr<LogEvent>;

        const char *getFile() const { return m_file; }

        int32_t getLine() const { return m_line; }

        uint32_t getElapse() const { return m_elapse; }

        uint32_t getThreadId() const { return m_threadId; }

        uint32_t getFiberId() const { return m_fiberId; }

        uint64_t getTime() const { return m_time; }

        const std::string &getThreadName() const { return m_threadName; }

        // 从stringstream流写入的内容转换为字符串被formatter当作MessageFormatItem解析，最终由appender输出到日志
        std::string getContent() const { return m_ss.str(); }

        std::stringstream &getSS() { return m_ss; }

        LogLevel::Level getLevel() const { return m_level; }

        std::shared_ptr<Logger> getLogger() const { return m_logger; }

        LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level, const char *file, int32_t line, uint32_t elapse,
                 uint32_t thread_id, uint32_t fiber_id, uint64_t time, const std::string &thread_name);

        /*
         * 格式化写入日志内容
         */
        void format(const char *fmt, ...);

        void format(const char *fmt, va_list al);

    private:
        // 文件名
        const char *m_file = nullptr;
        // 行号
        int32_t m_line = 0;
        // 程序启动依赖的耗时(毫秒)
        uint32_t m_elapse = 0;
        // 线程ID
        uint32_t m_threadId = 0;
        // 协程ID
        uint32_t m_fiberId = 0;
        // 时间戳
        uint64_t m_time;
        // 线程名
        std::string m_threadName;
        // 日志内容流
        std::stringstream m_ss;
        // 日志器
        std::shared_ptr<Logger> m_logger;
        // 日志级别
        LogLevel::Level m_level;
    };

    /**
    *  日志事件包装器,使日志支持流输入，利用析构函数，触发日志写入
    */
    class LogEventWrap {
    public:
        explicit LogEventWrap(LogEvent::ptr event) : m_event(event) {}

        ~LogEventWrap();

        LogEvent::ptr getEvent() { return m_event; };

        std::stringstream &getSS();

    private:
        LogEvent::ptr m_event;
    };

    /*
     * 日志格式化
     */
    class LogFormatter {
    public:
        using ptr = std::shared_ptr<LogFormatter>;

        explicit LogFormatter(const std::string &pattern);

        std::string format(LogLevel::Level level, LogEvent::ptr event);

        std::ostream &format(std::ostream &ofs, LogLevel::Level level, LogEvent::ptr event);

    public:
        class FormatterItem {
        public:
            using ptr = std::shared_ptr<FormatterItem>;

            virtual ~FormatterItem() = default;

            virtual void format(std::ostream &os, LogLevel::Level level, LogEvent::ptr event) = 0;
        };

        void init();

        bool isError() const { return m_error; }

        const std::string getPattern() const { return m_pattern; }

    private:
        std::string m_pattern;
        std::vector<FormatterItem::ptr> m_items;
        bool m_error = false;
    };

    /*
     * 日志输出目标
     */
    class LogAppender {
        friend class Logger;

    public:
        using ptr = std::shared_ptr<LogAppender>;
        using MutexType = Spinlock ;
        virtual ~LogAppender() = default;

        virtual void log(LogLevel::Level level, LogEvent::ptr event) = 0;

        void setFormatter(LogFormatter::ptr formatter);

        LogFormatter::ptr getFormatter();

        void setLevel(LogLevel::Level level) { m_level = level; }

        LogLevel::Level getLevel() const { return m_level; }

        bool hasFormatter() const { return m_hasFormatter; }

        virtual std::string toYamlString() = 0;

    protected:
        MutexType m_mutex;
        LogLevel::Level m_level = LogLevel::DEBUG;
        bool m_hasFormatter = false;
        LogFormatter::ptr m_formatter;
    };

    /*
     * 日志器
     */
    class Logger {
    public:
        using ptr = std::shared_ptr<Logger>;
        using MutexType = Spinlock ;

        explicit Logger(const std::string &name = "root");

        void log(LogLevel::Level level, LogEvent::ptr event);

        void debug(LogEvent::ptr event);

        void info(LogEvent::ptr event);

        void warn(LogEvent::ptr event);

        void error(LogEvent::ptr event);

        void fatal(LogEvent::ptr event);

        void addAppender(LogAppender::ptr appender);

        void delAppender(LogAppender::ptr appender);

        void clearAppenders();

        LogLevel::Level getLevel() const { return m_level; }

        void setLevel(LogLevel::Level level) { m_level = level; }

        const std::string &getName() { return m_name; }

        void setRoot(Logger::ptr root) { m_root = root; }

        void setFormatter(LogFormatter::ptr val);

        void setFormatter(const std::string &val);

        LogFormatter::ptr getFormatter();

        Logger::ptr getRoot() { return m_root; }

        virtual std::string toYamlString();

    private:
        MutexType m_mutex;
        std::string m_name;
        LogLevel::Level m_level;
        std::list<LogAppender::ptr> m_appenders;
        LogFormatter::ptr m_logFormatter;
        Logger::ptr m_root;
    };

    /*
     * 输出到控制台的LogAppender
     */
    class StdoutLogAppender : public LogAppender {
    public:
        using ptr = std::shared_ptr<StdoutLogAppender>;

        void log(LogLevel::Level level, LogEvent::ptr event) override;

        std::string toYamlString() override;
    };

    /*
     * 输出到文件的LogAppender
     */
    class FileLogAppender : public LogAppender {
    public:
        using ptr = std::shared_ptr<FileLogAppender>;

        explicit FileLogAppender(const std::string &filename);

        void log(LogLevel::Level level, LogEvent::ptr event) override;

        bool reopen();

        std::string toYamlString() override;


    private:
        std::string m_filename;
        std::ofstream m_filestream;
        uint64_t m_lastTime = 0;
    };

    class LogManager {
    public:
        using MutexType = Spinlock ;

        LogManager();

        Logger::ptr getLogger(const std::string &name);

        void init();

        Logger::ptr getRoot() const { return m_root; }

        std::string toYamlString();


    private:
        MutexType m_mutex;
        std::map<std::string, Logger::ptr> m_loggers;
        Logger::ptr m_root;
    };

    using LoggerMgr = sylar::Singleton<LogManager>;
}
#endif //SYLAR_LOG_H
